package com.example.demo.crc16;

import cn.hutool.json.JSONUtil;

import javax.swing.*;
import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

public class CRC16 {
    public static final byte ONE_BYTE_MASK = (byte) 0xFF;
    public static final int BYTE4_MASK =  0xFF000000;
    public static final int BYTE3_MASK =  0x00FF0000;
    public static final int BYTE2_MASK =  0x0000FF00;
    public static final int BYTE1_MASK =  0x000000FF;
    public static final int INT_LOW_BIT_MASK =  0x0000FFFF;
    public static final int ONE_INT_MASK =  0xFF;


    /**
     * byte[] 默认是大端序列
     */
    public static int bytesToInt(byte[] buf, int startIdx, int length) {
        if (buf == null) {
            return 0;
        }

        int len = Math.min(length, 4);

        byte[] bytes = {0x00, 0x00, 0x00, 0x00};

        for (int idx = 0; idx < len; idx++) {
            bytes[idx] = buf[startIdx + idx];
        }

        return ((((int) bytes[0]) << 24) & BYTE4_MASK)
                | ((((int) bytes[1]) << 16) & BYTE3_MASK)
                | ((((int) bytes[2]) << 8) & BYTE2_MASK)
                | (((int) bytes[3]) & BYTE1_MASK);

    }

    /**
     * 大端
     */
    public static byte[] intToBytes(int val) {
        byte[] bytes = {0x00, 0x00, 0x00, 0x00};
        bytes[0] = (byte) ((val >> 24) & ONE_INT_MASK);
        bytes[1] = (byte) ((val >> 16) & ONE_INT_MASK);
        bytes[2] = (byte) ((val >> 8) & ONE_INT_MASK);
        bytes[3] = (byte) (val & ONE_INT_MASK);

        return bytes;
    }

    /**
     * byte[] 默认是大端序列
     */
    public static short bytesToShort(byte[] buf, int startIdx) {
        int r = bytesToInt(buf, startIdx, 2);
        return (short) (INT_LOW_BIT_MASK & r);
    }

    /**
     * 大端
     */
    public static byte[] shortToBytes(short val) {
        byte[] bytes = {0x00, 0x00};
        bytes[0] = (byte) ((val >> 8) & ONE_INT_MASK);
        bytes[1] = (byte) (val & ONE_INT_MASK);

        return bytes;
    }

    /**
     * byte[] 默认是大端序列
     */
    public static long bytesToLong(byte[] buf, int startIdx, int length) {
        if (buf == null) {
            return 0;
        }
        int len = Math.min(length, 8);
        byte[] bytes = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};

        for (int idx = 0; idx < len; idx++) {
            bytes[idx] = buf[startIdx + idx];
        }

        long r = 0;
        for (int i = 0; i < len; i++) {
            r <<= 8;
            r |= (bytes[i] & BYTE1_MASK);
        }
        return r;
    }

    /**
     * 大端
     */
    public static byte[] longToBytes(long val) {

        byte[] bytes = new byte[8];
        for (int i = 0; i < bytes.length; i++) {
            int offset = 64 - (i + 1) * 8;
            bytes[i] =  (byte) ((val >> offset) & ONE_INT_MASK);
        }
        return bytes;
    }


    public static final byte BYTE_MASK = (byte) 0xFF;

    public static String bytesToHexStr(byte b) {
        byte[] src = {b};
        return bytesToHexStr(src);
    }

    public static String bytesToHexStr(byte[] src) {
        return bytesToHexStr(src, 0, src.length, Integer.MAX_VALUE, "");
    }


    public static String bytesToHexStr(byte[] data, int startIndex, int length, int step, String separator) {
        StringBuilder sb = new StringBuilder();
        if (data != null) {
            for (int i = 0; i < length && (i + startIndex) < data.length; i++) {
                if (i != 0 && i % step == 0) {
                    sb.append(separator);
                }
                String hv = Integer.toHexString(Byte.toUnsignedInt(data[startIndex + i]));
                if (hv.length() < 2) {
                    sb.append(0);
                }
                sb.append(hv);
            }
        }
        return sb.toString().toUpperCase();
    }

    public static byte[] hexStrToBytes(String hexString) {
        if (hexString == null || hexString.equals("")) {
            return null;
        }
        hexString = hexString.toUpperCase();
        int length = hexString.length() / 2;
        char[] hexChars = hexString.toCharArray();
        byte[] d = new byte[length];
        for (int i = 0; i < length; i++) {
            int pos = i * 2;
            d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]) & BYTE_MASK);
        }
        return d;
    }

    public static byte hexStrToOneByte(String hexString) {
        byte[] buf = hexStrToBytes(hexString);
        return buf[0];
    }

    private static byte charToByte(char c) {
        return (byte) "0123456789ABCDEF".indexOf(c);
    }

    // CRC16 / X25 多项式校验和
    public static int Crc16(byte [] bytes) {
        return Crc16(bytes, 0, bytes.length);
    }
    public static int Crc16(byte [] bytes, int startIdx, int len) {
        int i, j, lsb;
        int h = 0xffff;
        for (i = 0; i < len; i++) {
            h ^= (bytes[startIdx+i] & 0xff);
            h &= 0xffff;
            for (j = 0; j < 8; j++) {
                lsb = h & 0x0001; //取 CRC 的移出位
                h >>= 1;
                if (lsb == 1) {
                    h ^= 0x8408;
                    h &= 0xffff;
                }
            }
        }
        h ^= 0xffff;
        return h;
    }


    public static void main(String[] args) {
        byte[] data = {0x60 ,0x19 ,0x50 ,0x30 ,0x37 ,(byte) 0x86 ,0x13 ,(byte)0x89 ,(byte)0x86 ,0x04 ,0x23 ,0x19 ,0x19 ,0x23 ,0x39 ,0x79 ,0x67 ,0x00 ,0x01 ,0x00 ,0x00 ,0x00 ,0x01 };
        int crc = Crc16(data);
        System.out.println(bytesToHexStr(intToBytes(crc)));

        byte[] data1 = {0x00 ,0x01 ,0x00 ,0x18 ,0x08 ,0x60 ,0x19 ,0x50 ,0x30 ,0x37 ,(byte)0x86 ,0x13 ,(byte)0x89 ,(byte)0x86 ,0x04 ,0x23 ,0x19 ,0x19 ,0x23 ,0x39 ,0x79 ,0x67 ,0x00 ,0x01 ,0x00 ,0x00 ,0x00 ,0x01};
        crc = Crc16(data1);
        System.out.println(bytesToHexStr(intToBytes(crc)));

        byte[] data3 = {0x00 ,0x02 ,0x00 ,0x18 ,0x08 ,0x60 ,0x19 ,0x50 ,0x30 ,0x37 ,(byte)0x86 ,0x13 ,(byte)0x89 ,(byte)0x86 ,0x04 ,0x23 ,0x19 ,0x19 ,0x23 ,0x39 ,0x79 ,0x67 ,0x00 ,0x01 ,0x00 ,0x00 ,0x00 ,0x01};
        crc = Crc16(data3);
        System.out.println(bytesToHexStr(intToBytes(crc)));

        byte[] data2 = { 0x00, 0x01, 0x00, 0x04, 0x0a, 0x0b, 0x60, 0x60};
        crc = Crc16(data2);
        System.out.println(bytesToHexStr(intToBytes(crc)));

        byte[] data4 = {0x08, 0x00, 0x20, 0x0a, 0x7c, 0x6d};
        String mac = bytesToHexStr(data4, 0, 7, 1, ":");
        System.out.println(mac);
        System.out.println(bytesToHexStr(data4));

        System.out.println("---------------单位置信息---------------");
        byte[] time = intToBytes(1629810705);  // 2021-08-24 21:11:46
        byte[] lat = longToBytes(34205847);    // 34.205847
        byte[] lng = longToBytes(108835916);   // 108.835916
        byte[] alt = shortToBytes((short)96);      // 96
        byte[] sNum = {0b111};  // 7个卫星
        byte[] speed = {0b01};  // 1 KM/H
        byte[] heading = { (byte)0xAA, 0x00 }; // 东经 北纬 GPS已定位 GPS差分定位 Acc-on 511
        byte[] mode = {0x01};  // 补传
        byte[] type = {0x02};  // 拐点上传
        byte[] bNum = {0x01};  // 1个基站
        byte[] wNum = {0x01};  // 1个wifi
        byte[] lNum = {0x01};  // 1个蓝牙
        byte[] bdata = genBdata();
        byte[] wdata = genWdata();
        byte[] ldata = genLdata();

        byte[] combind = combind(time, lat, lng, alt, sNum, speed, heading ,mode, type, bNum, wNum, lNum, bdata, wdata, ldata);
        System.out.println("combind-lenght:" + combind.length);
        byte[] packagePrefix = { 0x00, 0x01, 0x00, 0x47 };
        combind = combind(packagePrefix, combind);
        for (byte b : combind) {
            System.out.print(bytesToHexStr(b));
            System.out.print(" ");
        }
        System.out.println();
        crc = Crc16(combind);
        System.out.println(bytesToHexStr(intToBytes(crc)));


        System.out.println("--------------test---------------");
        byte[] d = {0, 0, 0, 0, 2, 9, -16, -105};
        System.out.println(bytesToLong(d, 0, 8));
        for (byte b : lat) {
            System.out.print(b);
            System.out.print(" ");
        }
        System.out.println();
        System.out.println("---------------reverse----------------");
        byte b = (byte)0xFF;
        if (b < 0) {
            b = (byte)(b & 0x7F);
            System.out.println(b);
        }

        b = (byte)0xFF;
        BigDecimal bigDecimal = BigDecimal.valueOf(b);
        System.out.println(bigDecimal.divide(new BigDecimal("100")));

        byte[] command = {0x4C, 0x4A, 0x44, 0x57, 0x23, 0x00, 0x01};
        System.out.println(parseStringFromAsciiBytes(command, 0, command.length));
        System.out.println(new String(hexStrToBytes("4F4B2100")));

        String s = "00 53 00 4F 00 53 6C 42 65 51 00 3A 5E 7F 4E 1C 77 01 6D F1 57 33 5E 02 5B 9D 5B 89 53 3A 75 59 4E D9 59 27 90 53 8F 85 8D EF 00 2C 6C 11 6C BB 89 7F 53 57 00 31 00 32 00 33 00 32 7C 73";
        s = "4F 4D 7F 6E 00 3A 5E 7F 4E 1C 77 01 00 2E 60 E0 5D DE 5E 02 00 2E 60 E0 57 CE 53 3A 00 2E 4E 91 5C 71 89 7F 8D EF 00 2E 79 BB 60 E0 5D DE 5E 02 5B 66 59 27 65 59 80 B2 7E A6 00 32 00 35 7C 73 00 2E";
        s = "97 07 52 A8 62 A5 8B 66 00 3A 00 47 00 54 00 30 00 36 00 44 00 2D 00 31 00 32 00 38 00 33 00 36 00 2D 00 5A 00 4A 00 4D 00 2C 5E 7F 4E 1C 77 01 00 2E 60 E0 5D DE 5E 02 00 2E 60 E0 57 CE 53 3A 00 2E 4E 91 5C 71 89 7F 8D EF 00 2E 79 BB 60 E0 5D DE 5E 02 5B 66 59 27 65 59 80 B2 7E A6 00 32 00 37 7C 73 00 2E 00 2C 00 31 00 30 00 3A 00 34 00 33";
        s = "7C BE 78 6E 5B 9A 4F 4D FF 1A 5E 7F 4E 1C 77 01 6D F1 57 33 5E 02 5B 9D 5B 89 53 3A 75 59 4E D9 4E 00 8D EF 00 2E 00 47 00 34 00 2E 79 BB 9A D8 65 B0 59 47 5D E5 4E 1A 53 3A 7E A6 00 36 00 39 7C 73 00 2E";
        s = s.replaceAll(" ", "");
        System.out.println(new String(hexStrToBytes(s), StandardCharsets.UTF_16));
        s = "我是谁";
        String hexStr = bytesToHexStr(s.getBytes(StandardCharsets.UTF_16));
        System.out.println(new String(hexStrToBytes(hexStr), StandardCharsets.UTF_16));

        String commandArrStr = "APN\n" +
                "ASETAPN\n" +
                "SERVER\n" +
                "GMT\n" +
                "FACTORY\n" +
                "FACTORYALL\n" +
                "SOSALM\n" +
                "CALL\n" +
                "EURL\n" +
                "GPRSON\n" +
                "RESET\n" +
                "LINK\n" +
                "FN\n" +
                "SOS\n" +
                "SIMALM\n" +
                "PERIOD\n" +
                "TIMESET\n" +
                "JT\n" +
                "MONITOR\n" +
                "CLEARSIM#\n" +
                "CALLMODE\n" +
                "ALMSMS\n" +
                "GPSON\n" +
                "SMSCLEAR\n" +
                "HBT\n" +
                "TIMER\n" +
                "GTIMER\n" +
                "CLEAR\n" +
                "GFENCE\n" +
                "BATALM\n" +
                "CALL\n" +
                "FW\n" +
                "PWRONALM\n" +
                "PWROFFALM\n" +
                "RINGVOL\n" +
                "SUPERPASS\n" +
                "CALLVOL\n" +
                "FAMILY\n" +
                "FAMILYSMS\n" +
                "WIFI\n" +
                "WT\n" +
                "AL\n" +
                "SHUTDOWN\n" +
                "SETIMEI(只支持短信）\n" +
                "SF\n" +
                "ICCID#\n" +
                "IMSI\n" +
                "CHECK#\n" +
                "MSERVER\n" +
                "TEST\n" +
                "EMODE\n" +
                "NETSET\n" +
                "WG\n" +
                "SLPERIOD\n" +
                "MODE\n" +
                "STEPSET\n" +
                "TIMING TIMER\n" +
                "READY\n" +
                "WFKQ\n" +
                "LBSSW\n" +
                "SLEEP\n" +
                "PINLOCK\n" +
                "DSKG\n";
        String msg = "";
        byte[] commandByte = genCommandByte("80", 1, 1, "GPSON,300#");
        System.out.println(bytesToHexStr(commandByte));
        commandByte = genCommandByte("80", 2, 1, "LJDW#");
        System.out.println(bytesToHexStr(commandByte));
        commandByte = genCommandByte("80", 4, 1, "SERVER");
        System.out.println(bytesToHexStr(commandByte));
        commandByte = genCommandByte("80", 5, 1, "TIMESET#");
        System.out.println(bytesToHexStr(commandByte));
        System.out.println("设置时间");
        commandByte = genCommandByte("80", 6, 1, "TIMESET,2021,9,18,20,05,5#");
        System.out.println(bytesToHexStr(commandByte));
        commandByte = genCommandByte("80", 7, 1, "GTIMER,5#");
        System.out.println(bytesToHexStr(commandByte));

        System.out.println("---------------查询指令----------------");
        simpleCommand("VERSION#");
        simpleCommand("PARAM#");
        simpleCommand(("GPRSSET#"));
        simpleCommand("STATUS#");
        simpleCommand("WHERE#");
        simpleCommand("URL#");
        simpleCommand("DW#");
        simpleCommand("POSITION#");
        simpleCommand("FENCE#");
        simpleCommand("ASETAPN#");
        simpleCommand("ASETGMT#");
        simpleCommand("CHIP RID#");
        simpleCommand("SERVER,0,47.119.125.153,21100,0#");
        System.out.println("---------------设置查询指令----------------");
        simpleCommand("GTIMER,2#");
        simpleCommand("TIMER,1,30#");

        String[] commandArr = commandArrStr.split("\\n");
        System.out.println(commandArr.length);
//        String result = "";
//        int count = 0;
//        for (String c : commandArr) {
////            count++;
//            simpleCommand(c + "#");
////            if (count % 3 == 0) {
////                System.out.println(result);
////                result = "";
////            }
//        }
        simpleCommand("SERVER,0,40.72.104.172,8884,0#");


        hexStrToStrAndPrint("E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E E5 BD 93 E5 89 8D E6 97 B6 E9 97 B4 3A 32 30 32 31 2F 39 2F 31 38 20 31 38 3A 33 35 3A 35");
        hexStrToStrAndPrint("E5 BC 80 E5 90 AF 47 50 53 E5 B7 A5 E4 BD 9C 20 33 30 30 20 E5 88 86 E9 92 9F 20 E8 AE BE E7 BD AE E6 88 90 E5 8A 9F");
        hexStrToStrAndPrint("E5 8F 82 E6 95 B0 31 E9 94 99 E8 AF AF 28 32 30 32 30 7E 32 30 36 30 29");
        hexStrToStrAndPrint("E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E 47 54 49 4D 45 52 3A 35");
        hexStrToStrAndPrint("E6 8C 87 E4 BB A4 E6 A0 BC E5 BC 8F E9 94 99 E8 AF AF ");
        hexStrToStrAndPrint(" 5B 56 45 52 53 49 4F 4E 5D 4E 46 38 39 31 30 30 31 5F 53 33 34 31 5F 41 41 41 50 5F 56 31 2E 35 5F 32 31 30 36 31 37 2E 30 39 31 33 20 20 5B 42 55 49 4C 44 5D 3A 32 31 30 36 31 37 2E 30 39 31 33");
        hexStrToStrAndPrint("49 4D 45 49 3A 38 36 32 30 37 35 30 35 30 33 35 31 30 31 33 3B 20 4C 42 53 54 69 6D 65 72 3A 30 6D 69 6E 2C 47 50 53 54 69 6D 65 72 3A 33 30 73 65 63 3B 20 48 42 54 3A 35 6D 69 6E 3B 20 53 4F 53 3A 31 33 34 31 38 36 30 37 33 39 33 2C 31 35 38 31 33 33 32 34 33 37 32 2C 3B 20 46 61 6D 69 6C 79 4E 75 6D 62 65 72 3A E7 AE A1 E7 90 86 E5 91 98 3A 31 33 34 31 38 36 30 37 33 39 33 2C E5 90 B4 E4 BD B3 E5 84 BF 3A 31 35 38 31 33 33 32 34 33 37 32 2C 3A 3B 20 54 69 6D 65 5A 6F 6E 65 3A 45 38 2E 30 3B");
        hexStrToStrAndPrint("41 50 4E E8 87 AA E9 80 82 E5 BA 94 E5 B7 B2 E5 BC 80 E5 90 AF 2E");
        hexStrToStrAndPrint("E9 99 90 E5 88 B6 E5 85 B3 E6 9C BA 20 E5 BC 80 E5 85 B3 3A E5 B7 B2 E5 BC 80 E5 90 AF");
        hexStrToStrAndPrint(" E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E E9 99 90 E5 88 B6 E5 85 B3 E6 9C BA 20 E5 BC 80 E5 85 B3 3A E5 B7 B2 E5 BC 80 E5 90 AF");
        hexStrToStrAndPrint("E6 8E A5 E5 85 A5 E7 82 B9 E5 90 8D E7 A7 B0 3A 33 67 6E 65 74 2C E7 94 A8 E6 88 B7 E5 90 8D 3A 2C E5 AF 86 E7 A0 81 3A");
        hexStrToStrAndPrint("1C 00 00 00 01 E8 AE BE E7 BD AE E6 88 90 E5 8A 9F 2E 47 54 49 4D 45 52 3A 32 00 00 01");
        hexStrToStrAndPrint("46 4E 26 26 41 26 26 E7 AE A1 E7 90 86 E5 91 98 26 26 31 33 34 31 38 36 30 37 33 39 33 26 26 E5 90 B4 E4 BD B3 E5 84 BF 26 26 31 35 38 31 33 33 32 34 33 37 32 26 26 26 26 23 23");
        hexStrToStrAndPrint("54 49 4D 45 7C 30 7C 31 7C 31 30 3A 34 39 2D 31 30 3A 34 39 7C 7C 7C 7C 7C 7C 7C 5D 31 30 3A 35 30 2D 31 30 3A 35 30 7C 7C 7C 7C 7C 7C 7C 5D 7C 7C 7C 7C 7C 7C 7C 7D");
        hexStrToStrAndPrint("57 4E 26 26 41 26 26 E7 AE A1 E7 90 86 E5 91 98 26 26 31 35 38 31 33 33 32 34 33 37 37 26 26 E6 B5 8B E8 AF 95 E5 91 98 26 26 31 35 38 31 35 39 36 33 37 39 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 26 23 23");

        byte[] p01 = {0x11, 0x01, 0x08,0x62,0x07,0x50,0x50,0x36,0x12,0x69, (byte)0x80, 0x51,  0x32, 0x01,  0x00, 0x01};
        System.out.println(Crc16(p01));
        System.out.println(bytesToHexStr(intToBytes(Crc16(p01))));

        System.out.println("================================");

        printParam(0, 0, 0);
        printParam(1, 3, 1);
        printParam(1, 6, 2);



    }
    public static void printParam(int sst, int est,int pt) {
        for (int i = sst; i <= est; i++) {
            System.out.println(String.format("==========[st:%d]==[pt:%d]===========", i, pt));
            String tpl = "id=123456789012345&st=%d&key=a8a27c2c07d54e9c8fee91f3e1712f48&pt=%d";
            String report = String.format(tpl, i, pt);
            byte[] frame = new byte[report.getBytes().length + 5];
            frame[0] = 0x7f;
            byte[] test =  report.getBytes();
            short lenth = (short) (test.length);
            frame[1] = 0b0;
            frame[2] = (byte) ((lenth >> 8) & 0x00FF);
            frame[3] = (byte) (lenth & 0x00FF);
//            System.out.println(test.length);
//            System.out.println(bytesToHexStr(test));
//            System.out.println(getXorCheck(lenth, test));
            System.arraycopy(test, 0 ,frame , 4, lenth);
            frame[frame.length -1] = getXorCheck(lenth, test);
            System.out.println(bytesToHexStr(frame));
        }

    }

    public static byte getXorCheck(short contentLength, byte[] content) {
        byte high = (byte) (contentLength >> 8);
        byte low = (byte) (contentLength & 0x00FF);
        byte checkResult = high;
        checkResult ^= low;
        for (int i = 0; i < content.length; i++) {
            checkResult ^= content[i];
        }
        return checkResult;
    }


    public static String simpleCommand(String msg) {
        return simpleCommand(msg, 1);
    }

    public static String simpleCommand(String msg, int flag) {
        byte[] commandByte = genCommandByte("80", 2, flag, msg);
        String str = bytesToHexStr(commandByte);
        System.out.println(msg + " <----> " + str);
        return str;
    }
    public static void hexStrToStrAndPrint(String hexStr) {
        System.out.println(new String(hexStrToBytes(hexStr.replaceAll(" ", ""))));
    }

    public static void hexStrToAsciiStrAndPrint(String hexStr) {
        byte[] bytes = hexStrToBytes(hexStr.replaceAll(" ", ""));
        System.out.println(new String(bytes, StandardCharsets.UTF_8));
    }

    public static String parseStringFromAsciiBytes(byte[] data, int startIndex, int length) {
        try {
            StringBuffer sb = new StringBuffer();
            for (int i = 0; i < length; i++) {
                sb.append((char) data[startIndex+i]);
            }
            return sb.toString();
        } catch (Exception e) {
            return null;
        }
    }

    public static byte[] genBdata() {
        byte[] mcc = shortToBytes(Short.parseShort("10086"));
        byte[] mnc = shortToBytes(Short.parseShort("10087"));
        byte[] lac = shortToBytes(Short.parseShort("10088"));
        byte[] cellId = intToBytes(10089);
        byte[] signalStrength = { 0x01 };
        return combind(mcc, mnc, lac, cellId, signalStrength);
    }
    public static byte[] genWdata() {
        byte[] mac = hexStrToBytes("0800200A8C6D");
        byte[] ssidLength = { 0x04 };
        byte[] ssid = "ssid".getBytes();
        byte[] signalStrength = { 0x02 };
        return combind(mac, ssidLength, ssid, signalStrength);
    }
    public static byte[] genLdata() {
        byte[] mac = hexStrToBytes("0800200A8C6C");
        byte[] blueToothLength = { 0x09 };
        byte[] blueTooth = "blueTooth".getBytes();
        byte[] signalStrength = { 0x03 };
        return combind(mac, blueToothLength, blueTooth, signalStrength);
    }

    public static byte[] combind(byte[]... arg) {
        byte[] result = new byte[0];
        for (byte[] bytes : arg) {
            byte[] tmp = new byte[result.length + bytes.length];
            System.arraycopy(result, 0, tmp, 0, result.length);
            System.arraycopy(bytes, 0, tmp, result.length, bytes.length);
            result = tmp;
        }
        return result;
    }

    public static byte[] genCommandByte(int flag, String msg) {
        byte[] msgBytes = msg.getBytes();
//        byte[] msgBytes = msg.getBytes(StandardCharsets.UTF_16);
        int len = 4 + msgBytes.length + 2;
        byte[] buf = new byte[len + 1];
        buf[0] = (byte)len;
        byte[] flagBytes = intToBytes(flag);
        System.arraycopy(flagBytes, 0, buf, 1, 4);
        System.arraycopy(msgBytes, 0, buf, 5, msgBytes.length);
        buf[len] = 0x01;
        return buf;
    }
    public static byte[] genCommandByte(String cmdId, int seqNum, int flag, String msg) {

        byte[] bodyBytes = genCommandByte(flag, msg);
        // 拼接帧
        byte[] frameBuf;
        int bodyLen = bodyBytes.length + 5;
        byte[] bodyLenBytes;

        // init frameBuf
        frameBuf = new byte[4 + 1 + bodyLen];
        // start pos
        byte[] startDelimiterBytes = hexStrToBytes("7878");
        System.arraycopy(startDelimiterBytes, 0, frameBuf, 0 , 2);
        // body length num
        frameBuf[2] = (byte) bodyLen;
        bodyLenBytes = new byte[]{(byte) bodyLen};

        // cmd code
        frameBuf[2 + bodyLenBytes.length] = hexStrToOneByte(cmdId);

        // body
        System.arraycopy(bodyBytes, 0, frameBuf, 3 + bodyLenBytes.length, bodyBytes.length);

        // seq num
        byte[] seqNumBytes = intToBytes(seqNum);
        System.arraycopy(seqNumBytes, 2, frameBuf, 3 + bodyLenBytes.length + bodyBytes.length, 2);

        // crc16
        byte[] crc16Bytes = intToBytes(Crc16(frameBuf, 2, frameBuf.length - 6));
        System.arraycopy(crc16Bytes, 2, frameBuf, 5 + bodyLenBytes.length + bodyBytes.length, 2);

        // stop pos
        String hexStopDelimiter = "0D0A";
        byte[] stopDelimiterBytes = hexStrToBytes(hexStopDelimiter);
        System.arraycopy(stopDelimiterBytes, 0, frameBuf, 7 + bodyLenBytes.length + bodyBytes.length, 2);
        return frameBuf;
    }

    /**
     * 计算指令CRC
     * @param cmdId 指令号
     * @param seqNum 消息序列号
     * @param flag 服务器标识
     * @param msg 指令内容
     */
    public static byte[] genCommandByteCrc(String cmdId, int seqNum, int flag, String msg) {

        byte[] bodyBytes = genCommandByte(flag, msg);
        // 拼接帧
        byte[] frameBuf;
        int bodyLen = bodyBytes.length + 5;
        byte[] bodyLenBytes;

        // init frameBuf
        frameBuf = new byte[4 + 1 + bodyLen];
        // start pos
        byte[] startDelimiterBytes = hexStrToBytes("7878");
        System.arraycopy(startDelimiterBytes, 0, frameBuf, 0 , 2);
        // body length num
        frameBuf[2] = (byte) bodyLen;
        bodyLenBytes = new byte[]{(byte) bodyLen};

        // cmd code
        frameBuf[2 + bodyLenBytes.length] = hexStrToOneByte(cmdId);

        // body
        System.arraycopy(bodyBytes, 0, frameBuf, 3 + bodyLenBytes.length, bodyBytes.length);

        // seq num
        byte[] seqNumBytes = intToBytes(seqNum);
        System.arraycopy(seqNumBytes, 2, frameBuf, 3 + bodyLenBytes.length + bodyBytes.length, 2);

        // crc16
        byte[] crc16Bytes = intToBytes(Crc16(frameBuf, 2, frameBuf.length - 6));
        return crc16Bytes;
    }


}

